package com.twb.core.swagger2;

import java.util.HashSet;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.twb.core.security.TwbSecurityProperties;

import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @Title Swagger2Configuration
 * @Description Swagger2相关配置
 * @author DSY
 * @date 2018-10-17 16:18
 */
@Configuration
@EnableSwagger2
@EnableKnife4j
@ConditionalOnProperty(name = { "twb.swagger.enabled" }, havingValue = "true")
public class Swagger2Configuration {

	private static final Logger logger = LoggerFactory.getLogger(Swagger2Configuration.class);
	@Autowired
	private SwaggerConfigProperties configProperties;
	@Autowired
	private TwbSecurityProperties securityProperties;


	/**
	 * @Title  apiInfo
	 * @Description 设置api文档页面的标题描述.
	 * @return
	 * @author DSY
	 * @date 2018年11月13日 下午4:02:40
	 */
	public ApiInfo apiInfo() {
		return (new ApiInfoBuilder())
				.title(this.configProperties.getTitle())
				.description(this.configProperties.getDescription())
				.license(this.configProperties.getLicense())
				.licenseUrl(this.configProperties.getLicenseUrl())
				.termsOfServiceUrl("")
				.version(this.configProperties.getVersion())
				.contact(new Contact(this.configProperties.getContactName(),
						this.configProperties.getContactUrl(), 
						this.configProperties.getContactEmail()))
				.build();
	}
	
	/**
	 * @Title  customImplementation
	 * @Description 设置Docket对象中的swagger2需要生成api文档的包目录.
	 * @return Docket对象
	 * @author DSY
	 * @date 2018年11月13日 下午4:03:05
	 */
	@SuppressWarnings("deprecation")
	@Bean
	public Docket customImplementation() {
		if (logger.isDebugEnabled()) {
			logger.debug("swagger config custom implementation bean");
		}
		// 判断业务应用自定义的包路径
		HashSet<String> scanBasePackages;
		if (StringUtils.isEmpty(this.configProperties.getScanBasePackage())) {
			// 没有配置，只包含默认路径
			scanBasePackages = Sets.newHashSet(new String[] { "com.twb" });
		} else {
			// 配置多个包路径，半角逗号分隔。最后合并默认的路径
			scanBasePackages = Sets
					.newHashSet(StringUtils.commaDelimitedListToSet(this.configProperties.getScanBasePackage()));
//			scanBasePackages.add("com.twb");
		}

		List<Predicate<RequestHandler>> predicates = Lists.newArrayListWithExpectedSize(scanBasePackages.size());
		scanBasePackages.forEach((basePackage) -> {
			predicates.add(RequestHandlerSelectors.basePackage(basePackage));
		});
		//可以添加多个header或参数
		return (new Docket(DocumentationType.SWAGGER_2))
				.apiInfo(this.apiInfo())// 配置说明
				.select()// 选择那些路径和 api 会生成 document
				.apis(Predicates.or(predicates))// 需要拦截的包路径
				.paths(PathSelectors.any())// 拦截所有接口
				.build()// 构建
				.securitySchemes(this.securitySchemes())
				.securityContexts(this.securityContexts())
				.forCodeGeneration(true) // 将泛型中的类型扩展到文档中
				.protocols(Sets.newHashSet(new String[] { "http", "https" }))
		/* .globalOperationParameters(parameters) */;
	}

	public List<ApiKey> securitySchemes() {
		ApiKey[] apiKeys = new ApiKey[] { 
				new ApiKey(securityProperties.getAppIdName(), securityProperties.getAppIdName(), "header"),
				new ApiKey(securityProperties.getAppTokenName(), securityProperties.getAppTokenName(), "header"),
				new ApiKey(securityProperties.getAppSecretName(), securityProperties.getAppSecretName(), "header"),
				new ApiKey(securityProperties.getTokenName(), securityProperties.getTokenName(), "header")
				};
		return Lists.newArrayList(apiKeys);
	}

	public List<SecurityContext> securityContexts() {
		return Lists.newArrayList(new SecurityContext[] { SecurityContext.builder()
				.securityReferences(this.defaultAuth()).forPaths(PathSelectors.regex("^(?!auth).*$")).build() });
	}

	private List<SecurityReference> defaultAuth() {
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[] { authorizationScope };
		SecurityReference[] securityReferences = new SecurityReference[] { 
				new SecurityReference(securityProperties.getAppIdName(), authorizationScopes),
				new SecurityReference(securityProperties.getAppTokenName(), authorizationScopes),
				new SecurityReference(securityProperties.getAppSecretName(), authorizationScopes),
				new SecurityReference(securityProperties.getTokenName(), authorizationScopes)
				};
		return Lists
				.newArrayList(securityReferences);
	}

}
